#include "PropertyBuilder.h"
#include "ScriptCompiler/ScriptFileContext.h"
#include "ScriptCompiler/AST/PropertyNode.h"
#include "ScriptCompiler/ASTBuilder/GetAccessorBuilder.h"
#include "ScriptCompiler/ASTBuilder/SetAccessorBuilder.h"
#include "ScriptCompiler/ASTBuilder/ExpressionBuilder.h"
#include "ScriptCompiler/ASTBuilder/PropertyIndexerBuilder.h"

CPropertyBuilder::CPropertyBuilder(std::shared_ptr<CScriptFileContext> InContext)
{
    Context = InContext;
    Result= std::make_shared<CPropertyNode>(Context);
}

bool CPropertyBuilder::ReadCurentToken()
{
    bool IsIndexer = false;
    //read property name
    {
        auto Token = Context->GetCurrentToken();

        if(Token->Type == EScriptTokenType::This)
        {
            IsIndexer= true;
        }
        else if(Token->Type == EScriptTokenType::Identifier)
        {

        }
        else
        {
            Context->RaiseError(10080,Token,U"Expected property name");
            return false;
        }

        Result->Name = Token;

        Context->CurrentTokenIndex++;

    }

    //try read indexer
    if(IsIndexer)
    {
        auto IndexerBuilder = std::make_shared<CPropertyIndexerBuilder>(Context);
        if(!IndexerBuilder->ReadCurentToken())
        {
            return false;
        }
        Result->Indexer = IndexerBuilder->Result;
        
    }


    auto NextToken = Context->GetCurrentToken();
    if (Context->GetCurrentToken()->Type == EScriptTokenType::LeftBrace)
    {
        Context->CurrentTokenIndex++; //skip '{'

        //check next builder is '}'
        {
            auto RightBraceoken = Context->GetCurrentToken();
            if (RightBraceoken->Type == EScriptTokenType::RightBrace)
            {
                Context->RaiseError(10081,NextToken,U"property at leaset have one accessor");
                return false;
            }
        }
        
        while (true)
        {
            auto Token = Context->GetCurrentToken();

            if(Token->Type == EScriptTokenType::RightBrace)
            {
                break;
            }

            switch(Token->Type)
            {
                case EScriptTokenType::Get:
                {
                    auto AccessorBuilder= std::make_shared<CGetAccessorBuilder>(Context);
                    if(!AccessorBuilder->ReadCurentToken())
                    {
                        return false;
                    }
                    Result->Getter = AccessorBuilder->Result;
                    break;
                }
                case EScriptTokenType::Init:
                case EScriptTokenType::Set:
                {
                    auto AccessorBuilder= std::make_shared<CSetAccessorBuilder>(Context);
                    if(!AccessorBuilder->ReadCurentToken())
                    {
                        return false;
                    }
                    Result->Setter = AccessorBuilder->Result;
                    break;
                }
            
                default:
                {
                    Context->RaiseError(10082,Token,U"unsupported token, only get/set/init accesor is supported");
                    return false;
                }
            }
        }

        Context->CurrentTokenIndex++; //skip right brace
        return true; 
    }
    else if (NextToken->Type == EScriptTokenType::LambdaOperator)
    {
        //skip '=>'
        Context->CurrentTokenIndex++;

        //find end ';'
        auto SemiColon = FindNextToken(EScriptTokenType::Semicolon,Context);
        if(!SemiColon)
        {
            Context->RaiseError(10083,Context->GetCurrentToken(),U"Expected ';' at end of property declaration");
            return false;
        }

        auto ExpressionBuilder= std::make_shared<CExpressionBuilder>(Context,SemiColon.GetValue());
        if(!ExpressionBuilder->ReadCurentToken())
        {
            return false;
        }
        Result-> DefaultValue = ExpressionBuilder->Result;
        return true;
    }
    else
    {
        Context->RaiseError(10084,NextToken,U"Expected '=>' or '{' after property name");
        return false;
    }
    
    return true;

}